package crp.services.api.graphql;

import com.qwlabs.graphql.relay.Connection;
import com.qwlabs.graphql.relay.Relays;
import crp.core.portal.app.NewsService;
import crp.core.portal.domain.messages.PortalMessages;
import crp.core.portal.domain.models.NewsArticleQuery;
import crp.core.security.domain.models.Permissions;
import crp.core.shared.LifecycleStatus;
import crp.services.api.graphql.models.CreateNewsArticleInput;
import crp.services.api.graphql.models.DeleteNewsArticleInput;
import crp.services.api.graphql.models.EnableNewsArticleInput;
import crp.services.api.graphql.models.NewsArticlePayload;
import crp.services.api.graphql.models.UpdateNewsArticleInput;
import org.eclipse.microprofile.graphql.DefaultValue;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Mutation;
import org.eclipse.microprofile.graphql.Query;

import javax.annotation.security.RolesAllowed;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Optional;
import java.util.Set;

@GraphQLApi
@RequestScoped
@Transactional
public class NewsGraphQLApi {
    private final NewsService newsService;

    @Inject
    public NewsGraphQLApi(NewsService newsService) {
        this.newsService = newsService;
    }

    @Mutation
    @NotNull
    @RolesAllowed(Permissions.PORTAL_NEWS_EDIT)
    public NewsArticlePayload createNewsArticle(@NotNull @Valid CreateNewsArticleInput input) {
        return NewsArticlePayload.of(newsService.articleEditor().create(input.createCommand()));
    }

    @Mutation
    @NotNull
    @RolesAllowed(Permissions.PORTAL_NEWS_EDIT)
    public NewsArticlePayload updateNewsArticle(@NotNull @Valid UpdateNewsArticleInput input) {
        return newsService.articleEditor().update(input.createCommand())
                .map(NewsArticlePayload::of)
                .orElseThrow(() -> PortalMessages.INSTANCE.newsArticleNotFound(input.getId()));
    }

    @Mutation
    @NotNull
    @RolesAllowed(Permissions.PORTAL_NEWS_EDIT)
    public NewsArticlePayload enableNewsArticle(@NotNull @Valid EnableNewsArticleInput input) {
        return newsService.articleEditor().enable(input.getId())
                .map(NewsArticlePayload::of)
                .orElseThrow(() -> PortalMessages.INSTANCE.newsArticleNotFound(input.getId()));
    }

    @Mutation
    @NotNull
    @RolesAllowed(Permissions.PORTAL_NEWS_EDIT)
    public Boolean deleteNewsArticle(@NotNull @Valid DeleteNewsArticleInput input) {
        newsService.articleEditor().delete(input.getId());
        return true;
    }

    @Query
    @NotNull
    public Connection<NewsArticlePayload> newsArticles(
            Optional<String> keyword,
            @DefaultValue("0") String after,
            @DefaultValue("10") Integer first) {
        var query = NewsArticleQuery.builder()
                .keyword(keyword.orElse(null))
                .range(Relays.of(after, first))
                .build();
        return Relays.of(newsService.articleSearcher().by(query), NewsArticlePayload::of);
    }

    @Query
    @NotNull
    public Connection<NewsArticlePayload> enabledNewsArticles(
            Optional<String> keyword,
            @DefaultValue("0") String after,
            @DefaultValue("10") Integer first) {
        var query = NewsArticleQuery.builder()
                .keyword(keyword.orElse(null))
                .statuses(Set.of(LifecycleStatus.ENABLED))
                .range(Relays.of(after, first))
                .build();
        return Relays.of(newsService.articleSearcher().by(query), NewsArticlePayload::of);
    }

    @Query
    @NotNull
    public NewsArticlePayload newsArticle(@NotBlank String id) {
        return newsService.articleSearcher().byId(id)
                .map(NewsArticlePayload::of)
                .orElse(null);
    }
}
